﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Controls.Primitives;
using System.Windows.Media;

namespace DateRangeSlider
{
  public class DateRange : Thumb
  {
    #region ' Const '

    private static readonly Pen Pen = new Pen(Brushes.SkyBlue, 1);
    private static readonly Typeface Typeface = new Typeface("Tahoma");

    private const FrameworkPropertyMetadataOptions Options =
      FrameworkPropertyMetadataOptions.AffectsRender |
      FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender;

    #endregion

    #region ' Minimum '

    public static readonly DependencyProperty MinimumProperty =
      DependencyProperty.Register("Minimum", typeof(DateTime), typeof(DateRange), 
      new FrameworkPropertyMetadata(DateTime.MinValue, Options));

    public DateTime Minimum
    {
      get { return (DateTime)GetValue(MinimumProperty); }
      set { SetValue(MinimumProperty, value); }
    }

    #endregion

    #region ' Maximum '

    public static readonly DependencyProperty MaximumProperty =
      DependencyProperty.Register("Maximum", typeof(DateTime), typeof(DateRange), 
      new FrameworkPropertyMetadata(DateTime.MaxValue, Options));

    public DateTime Maximum
    {
      get { return (DateTime)GetValue(MaximumProperty); }
      set { SetValue(MaximumProperty, value); }
    }

    #endregion

    #region ' AbsoluteMin '

    public static readonly DependencyProperty AbsoluteMinProperty =
      DependencyProperty.Register("AbsoluteMin", typeof(double),
      typeof(DateRange), new FrameworkPropertyMetadata(0.0, Options));

    public double AbsoluteMin
    {
      get { return (double)GetValue(AbsoluteMinProperty); }
      set { SetValue(AbsoluteMinProperty, value); }
    }

    #endregion

    #region ' AbsoluteMax '

    public static readonly DependencyProperty AbsoluteMaxProperty =
      DependencyProperty.Register("AbsoluteMax", typeof(double),
      typeof(DateRange), new FrameworkPropertyMetadata(0.0, Options));

    public double AbsoluteMax
    {
      get { return (double)GetValue(AbsoluteMaxProperty); }
      set { SetValue(AbsoluteMaxProperty, value); }
    }

    #endregion

    #region ' ViewPortMin '

    public static readonly DependencyProperty ViewPortMinProperty =
      DependencyProperty.Register("ViewPortMin", typeof (double), 
      typeof (DateRange), new FrameworkPropertyMetadata(0.0, Options));

    public double ViewPortMin
    {
      get { return (double) GetValue(ViewPortMinProperty); }
      set { SetValue(ViewPortMinProperty, value); }
    }

    #endregion

    #region ' ViewPortMax '

    public static readonly DependencyProperty ViewPortMaxProperty =
      DependencyProperty.Register("ViewPortMax", typeof (double), 
      typeof (DateRange), new FrameworkPropertyMetadata(0.0, Options));

    public double ViewPortMax
    {
      get { return (double) GetValue(ViewPortMaxProperty); }
      set { SetValue(ViewPortMaxProperty, value); }
    }

    #endregion

    #region ' RangeMin '

    public static readonly DependencyProperty RangeMinProperty =
      DependencyProperty.Register("RangeMin", typeof(double),
      typeof(DateRange), new FrameworkPropertyMetadata(0.0, Options));

    public double RangeMin
    {
      get { return (double)GetValue(RangeMinProperty); }
      set { SetValue(RangeMinProperty, value); }
    }

    #endregion

    #region ' RangeMax '

    public static readonly DependencyProperty RangeMaxProperty =
      DependencyProperty.Register("RangeMax", typeof(double),
      typeof(DateRange), new FrameworkPropertyMetadata(0.0, Options));

    public double RangeMax
    {
      get { return (double)GetValue(RangeMaxProperty); }
      set { SetValue(RangeMaxProperty, value); }
    }

    #endregion

    public double MinDelta { get { return (AbsoluteMax - AbsoluteMin)/(Maximum - Minimum).Ticks*TimeSpan.FromDays(1).Ticks; }}

    private Tr _rangeToDate;
    private Tr _rangeToScreen;
    private Tr _dateToScreen;
    private IEnumerable<Date> _ticks;

    public DateRange()
    {
      Initialized += OnInitialized;
    }

    private void OnInitialized(object sender, EventArgs eventArgs)
    {
      InvalidateVisual();
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
      _rangeToDate = new Tr(Minimum.Ticks,
        (Maximum - Minimum).Ticks / (AbsoluteMax - AbsoluteMin));
      _rangeToScreen = new Tr(-ViewPortMin) *
        new Tr(0, finalSize.Width / (ViewPortMax - ViewPortMin));
      _dateToScreen = _rangeToDate.Invert() * _rangeToScreen;

      return base.ArrangeOverride(finalSize);
    }
    protected override void OnRender(DrawingContext drawingContext)
    {
      base.OnRender(drawingContext);

      drawingContext.DrawLine(Pen, 
        new Point(0, ActualHeight), 
        new Point(ActualWidth, ActualHeight));

      _ticks = RulerUtils.Ticks(
        _rangeToDate.ToDate(ViewPortMin), 
        _rangeToDate.ToDate(ViewPortMax),
        ActualWidth, Format);

      foreach (var tick in _ticks) 
        Draw(drawingContext, tick);
    }

    private void Draw(DrawingContext drawingContext, Date date)
    {
      var x = _dateToScreen.Transform(date.Data.Ticks);
      drawingContext.DrawLine(Pen, new Point(x, 0), new Point(x, ActualHeight));
      drawingContext.DrawText(
        Format(date.ToString()), new Point(x + 3, ActualHeight - 17));
    }
    private static FormattedText Format(string text)
    {
      return new FormattedText(text,
        CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
        Typeface, 12, Brushes.SkyBlue);
    }
  }
}